
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       support.c
  * @author     baiyang
  * @date       2023-4-24
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "support.h"
#include "hw_config.h"
#include "bl_protocol.h"

#include <rtthread.h>
#include <rtdevice.h>
/*-----------------------------------macro------------------------------------*/
#define REVID_MASK    0xFFFF0000
#define DEVID_MASK    0xFFF

#define FIRST_BAD_SILICON_OFFSET 1
/*----------------------------------typedef-----------------------------------*/
typedef uint16_t mcu_rev_e;

typedef struct mcu_des_t {
    uint16_t mcuid;
    const char *desc;
    char  rev;
} mcu_des_t;

// The default CPU ID  of STM32_UNKNOWN is 0 and is in offset 0
// Before a rev is known it is set to ?
// There for new silicon will result in STM32F4..,?
mcu_des_t mcu_descriptions[] = {
    { SOC_MCU_ID,    SOC_MCU_DESC,    '?'},
};

typedef struct mcu_rev_t {
    mcu_rev_e revid;
    char  rev;
} mcu_rev_t;

/*
 * This table is used in 2 ways. One to look look up the revision
 * of a given chip. Two to see it a revsion is in the group of "Bad"
 * silicon.
 *
 * Therefore when adding entries for good silicon rev, they must be inserted
 * at the beginning of the table. The value of FIRST_BAD_SILICON_OFFSET will
 * also need to be increased to that of the value of the first bad silicon offset.
 *
 */
const mcu_rev_t silicon_revs[] = {
    {SOC_MCU_REVID, '?'}, /* Revision */
};
/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
rt_device_t puart;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void led_init(void)
{
    // setup the main LEDs as outputs
    rt_pin_mode(BOARD_PIN_LED_ACTIVITY,   PIN_MODE_OUTPUT);
    rt_pin_mode(BOARD_PIN_LED_BOOTLOADER, PIN_MODE_OUTPUT);

    // turn all lights off
    rt_pin_write(BOARD_PIN_LED_ACTIVITY,   BOARD_GPIO_LED_OFF);
    rt_pin_write(BOARD_PIN_LED_BOOTLOADER, BOARD_GPIO_LED_OFF);
}

void led_on(unsigned led)
{
    switch (led) {
    case LED_ACTIVITY:
        rt_pin_write(BOARD_PIN_LED_ACTIVITY,   BOARD_GPIO_LED_ON);
        break;

    case LED_BOOTLOADER:
        rt_pin_write(BOARD_PIN_LED_BOOTLOADER, BOARD_GPIO_LED_ON);
        break;
    }
}

void led_off(unsigned led)
{
    switch (led) {
    case LED_ACTIVITY:
        rt_pin_write(BOARD_PIN_LED_ACTIVITY,   BOARD_GPIO_LED_OFF);
        break;

    case LED_BOOTLOADER:
        rt_pin_write(BOARD_PIN_LED_BOOTLOADER, BOARD_GPIO_LED_OFF);
        break;
    }
}

void led_toggle(unsigned led)
{
    switch (led) {
    case LED_ACTIVITY:
        rt_pin_write(BOARD_PIN_LED_ACTIVITY,   (rt_base_t)!rt_pin_read(BOARD_PIN_LED_ACTIVITY));
        break;

    case LED_BOOTLOADER:
        rt_pin_write(BOARD_PIN_LED_BOOTLOADER, (rt_base_t)!rt_pin_read(BOARD_PIN_LED_BOOTLOADER));
        break;
    }
}

void uart_cinit(void *config)
{
    (void)config;
    puart = rt_device_find("uart1");

    if (puart != NULL) {
        rt_device_open(puart, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
    }
}

void uart_cfini(void)
{
    if (puart != NULL) {
        rt_device_close(puart);
    }
}

int uart_cin(void)
{
    if (puart == NULL) {
        return -1;
    }

    uint8_t rec_data;

    if (rt_device_read(puart, 0, &rec_data, sizeof(rec_data)) == sizeof(rec_data)) {
        return (int)rec_data;
    }

    return -1;
}

void uart_cout(uint8_t *buf, unsigned len)
{
    if (puart != NULL) {
        rt_device_write(puart, 0, buf, len);
    }
}

uint32_t get_mcu_id(void)
{
    return *(uint32_t *)SOC_CHIPID_START_ADDRESS;
}

int get_mcu_desc(int max, uint8_t *revstr)
{
    uint32_t idcode = (*(uint32_t *)SOC_CHIPID_START_ADDRESS);
    int32_t mcuid = idcode & DEVID_MASK;
    mcu_rev_e revid = (idcode & REVID_MASK) >> 16;

    mcu_des_t des = mcu_descriptions[0];

    for (int i = 0; i < arraySize(mcu_descriptions); i++) {
        if (mcuid == mcu_descriptions[i].mcuid) {
            des = mcu_descriptions[i];
            break;
        }
    }

    for (int i = 0; i < arraySize(silicon_revs); i++) {
        if (silicon_revs[i].revid == revid) {
            des.rev = silicon_revs[i].rev;
        }
    }

    uint8_t *endp = &revstr[max - 1];
    uint8_t *strp = revstr;

    while (strp < endp && *des.desc) {
        *strp++ = *des.desc++;
    }

    if (strp < endp) {
        *strp++ = ',';
    }

    if (strp < endp) {
        *strp++ = des.rev;
    }

    return  strp - revstr;
}

int check_silicon(void)
{
#if defined(TARGET_HW_PX4_FMU_V2)  || defined(TARGET_HW_PX4_FMU_V3) || defined(TARGET_HW_PX4_FMU_V4) || defined(TARGET_HW_UVIFY_CORE)
    uint32_t idcode = (*(uint32_t *)SOC_CHIPID_START_ADDRESS);
    mcu_rev_e revid = (idcode & REVID_MASK) >> 16;

    for (int i = FIRST_BAD_SILICON_OFFSET; i < arraySize(silicon_revs); i++) {
        if (silicon_revs[i].revid == revid) {
            return -1;
        }
    }

#endif
    return 0;
}
/*------------------------------------test------------------------------------*/


